import_string
先来分析一下这个动态导入函数:werkzeug.utils.import_string
def import_string(import_name, silent=False):
"""基于字符串动态导入对象。字符串有两种形式写法用点来分割对象名(``xml.sax.saxutils.escape``)和用冒号来分割对象名
(``xml.sax.saxutils:escape``).
silent决定发生异常时是返回None还是抛出一些异常(安静,挺形象的。)
"""
import_name = str(import_name).replace(':', '.')
try:
try:
#首先假设import_name是一个包名或者模块名(非对象名)。
__import__(import_name)
except ImportError:
if '.' not in import_name: #没有点说明即使我们假设错误,也没法继续尝试。故而立即抛出异常
raise
else:
#如果导入正确,那么可以从sys.modules字典里面获取已经导入的模块或包.
#为什么要通过sys.modules[import_name]返回而不是直接return __import__(import_name)呢?因为__import__在没有提供fromlist参数时,返回值策略为返回第一个点之前的顶级包对象,比如'os.path'返回的是os而不是os.path.
return sys.modules[import_name]
#如果import_name不是包名或模块名,而是具体的对象、函数名。
module_name, obj_name = import_name.rsplit('.', 1)
try:
#相当于from module_name import obj_name
module = __import__(module_name, None, None, [obj_name])
except ImportError:
#如果支持的模块没有被父模块设置好的情形中会导入失败,这时需要先导入父模块(或包)。典型的例子就是logging与logging.config。
module = import_string(module_name)
try:
#通过getarr从模块中返回具体对象
return getattr(module, obj_name)
except AttributeError as e:
raise ImportError(e)
except ImportError as e:
if not silent:
reraise(
ImportStringError,
ImportStringError(import_name, e),
sys.exc_info()[2])
flask.config模块
import os
import types
import errno
from werkzeug.utils import import_string
from ._compat import string_types, iteritems
from . import json
class Config(dict):
"""config类除了正常的dict职责外,还具有从py文件(模块)、对象或字典填充值的能力。
from_envvar会调用from_pyfile会调用from_object,另还有from_json会调用from_mapping
还有一个便利的get_namespace方法。
"""
def __init__(self, root_path, defaults=None): # root_path查找py文件的基路径。
dict.__init__(self, defaults or {})
self.root_path = root_path
def from_envvar(self, variable_name, silent=False):
"""variable_name是一个环境变量名,指向一个py文件。返回bool值,是否加载填充成功。
"""
rv = os.environ.get(variable_name)
#...省略部分校验逻辑...
#这里调用from_pyfile
return self.from_pyfile(rv, silent=silent)
def from_pyfile(self, filename, silent=False):
filename = os.path.join(self.root_path, filename)
#利用标准库types来动态创建一个模块对象,名为config,文件属性(__file__)指向设置为前面的filename
d = types.ModuleType('config')
d.__file__ = filename
try:
with open(filename) as config_file:
#exec,compile都是内置函数。
#exec(object[, globals[, locals]]) 参数object可以为str或者字节码对象,返回None。这里可以理解为解释字节码,并将字节码执行过程中的全局变量存入d.__dict__字典中。
#compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
#compile用于动态编译文件为字节码对象,其中'exec'为编译模式,代表整个文件(多行)编译,其余模式还有'eval'单行语句编译,'single'单行交互式语句编译。
exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
except IOError as e:
...略pass...
#这里调用from_object,from_object才是关键。
self.from_object(d)
return True
def from_object(self, obj):
"""参数obj可以是str(需要被导入的对象)或者一个对象。它只允许模块或对象中的大写属性填充进来,小写的忽略。
你可以这样来使用本方法
app.config.from_object('yourapplication.default_config')或者
from yourapplication import default_config
app.config.from_object(default_config)
"""
if isinstance(obj, string_types):
obj = import_string(obj)
for key in dir(obj):
if key.isupper(): #只允许模块或对象中的大写属性填充进来,小写的忽略。
self[key] = getattr(obj, key)
def from_json(self, filename, silent=False):
"""filename是json文件名。这是flask0.11添加的方法。
"""
filename = os.path.join(self.root_path, filename)
try:
with open(filename) as json_file:
obj = json.loads(json_file.read()) #注意,如果不知道json.loads的type参数,那么默认加载为字典。即obj是一个字典
except IOError as e:
...略pass...
return self.from_mapping(obj) #这里调用from_mapping
def from_mapping(self, *mapping, **kwargs):
"""行为类似于dict的update方法,只不过忽略小写属性.flask0.11添加的方法
"""
mappings = []
if len(mapping) == 1:
if hasattr(mapping[0], 'items'):
mappings.append(mapping[0].items())
else:
mappings.append(mapping[0])
elif len(mapping) > 1:
raise TypeError(
'expected at most 1 positional argument, got %d' % len(mapping)
)
mappings.append(kwargs.items())
for mapping in mappings:
for (key, value) in mapping:
if key.isupper():
self[key] = value
return True
def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
"""返回一个字典,这个字典包含该命名空间的items。这个命名空间是以_作为分隔标识的.比如
app.config['IMAGE_STORE_TYPE'] = 'fs'
app.config['IMAGE_STORE_PATH'] = '/var/app/images'
app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
image_store_config = app.config.get_namespace('IMAGE_STORE_')
结果 `image_store_config` 会是这样::
{
'type': 'fs',
'path': '/var/app/images',
'base_url': 'http://img.website.com'
}
当需要利用这个进行传参时是比较方便的。
trim_namespace参数为True时,行为就是上面所述,为False时返回的key会包含namespace前缀。
"""
rv = {}
for k, v in iteritems(self):
if not k.startswith(namespace):
continue
if trim_namespace:
key = k[len(namespace):]
else:
key = k
if lowercase:
key = key.lower()
rv[key] = v
return rv
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
config模块阅读配置到此结束,下一篇主题待定。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。